/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.rafkind.resrev.dao.impl;

import com.rafkind.resrev.dao.ReservationDao;
import com.rafkind.resrev.data.Reservation;
import com.rafkind.resrev.data.Resource;
import com.rafkind.resrev.data.Status;
import com.rafkind.resrev.data.User;
import java.util.Date;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

/**
 *
 * @author Fictional Hero
 */
public class ReservationDaoImpl implements ReservationDao {
  private EntityManager manager;
  
  
  public void saveReservation(Reservation r) {
    if (r.isNew())
      getEntityManager().persist(r);
    else
      getEntityManager().merge(r);
  }
  
  public List<Reservation> findPossibleConflicts(
      Reservation existingReservation,
      Resource resource, 
      Date start, 
      Date end) {
    
    //System.out.println("Checking for conflicts with " + resource + " from " + start + " to " + end);
    
    Query query = getEntityManager()
        .createQuery("select r from Reservation r where "
        + (existingReservation.isNew() ? "" : " r != :existing and ")
        + " r.resource = :resource and "
        + " (r.start < :end and r.end > :start) and " 
        + " (r.authorization = :pending or r.authorization = :approved) "
        + " order by r.start asc"        
        );
    if (!existingReservation.isNew())
      query.setParameter("existing", existingReservation);
    query.setParameter("resource", resource);
    query.setParameter("start", start);
    query.setParameter("end", end);
    query.setParameter("pending", Status.PENDING);
    query.setParameter("approved", Status.APPROVED);    
    
    return query.getResultList();
  }
  
  public List<Reservation> findReservations(Date start, 
      Date end) {
    Query query = getEntityManager()
        .createQuery("select r from Reservation r where "
        + " (r.start < :end and r.end >= :start) and " 
        + " (r.authorization = :pending or r.authorization = :approved) "
        + " order by r.end asc");
    
    query.setParameter("start", start);
    query.setParameter("end", end);
    query.setParameter("pending", Status.PENDING);
    query.setParameter("approved", Status.APPROVED);
    
    return query.getResultList();
  }

  public int countAllReservations(User user) {
    Query query = getEntityManager()
        .createQuery("select count(r) from Reservation r "
        + (user.isAdmin() ? " " : "where (r.reserver = :user) or (r.authorizor = :user) or (r.authorizor is null and (r.resource.authorizor=:user or r.reserver.authorizor=:user))"));
    if (!user.isAdmin())
      query.setParameter("user", user);
    
    return ((Long)query.getSingleResult()).intValue();
  }

  public int countApprovableReservations(User user) {
    Query query = getEntityManager()
        .createQuery("select count(r) from Reservation r where "
        + " (r.end > :now) and "
        + " (r.reserver.authorizor = :user1 or r.resource.authorizor = :user2) and "
        + " (r.authorization = :pending) "
        );
    query.setParameter("now", new Date());
    query.setParameter("user1", user);
    query.setParameter("user2", user);
    query.setParameter("pending", Status.PENDING);
    
    return ((Long)query.getSingleResult()).intValue();
  }

  public int countPendingReservations(User user) {
    Query query = getEntityManager()
        .createQuery("select count(r) from Reservation r where "
        + " (r.end > :now) and "
        + " (r.reserver = :user) and "
        + " (r.authorization = :pending) "
        );
    query.setParameter("now", new Date());
    query.setParameter("user", user);
    query.setParameter("pending", Status.PENDING);
    
    return ((Long)query.getSingleResult()).intValue();
  }

  public List<Reservation> getAllReservations(User user, int first, int count, String sortProperty, boolean isAscending) {
    Query query = getEntityManager()
        .createQuery("select r from Reservation r "
        + (user.isAdmin() ? " " : "where (r.reserver = :user) or (r.authorizor = :user) or (r.authorizor is null and (r.resource.authorizor=:user or r.reserver.authorizor=:user))")
        + " order by r." + sortProperty
        + " " + (isAscending ? " ASC" : " DESC")
        );
    query.setFirstResult(first);
    query.setMaxResults(count);
    if (!user.isAdmin())
      query.setParameter("user", user);
    
    return query.getResultList();
  }

  public List<Reservation> getApprovableReservations(
      User user, 
      int first, 
      int count, 
      String sortProperty, 
      boolean isAscending) {
    
    Query query = getEntityManager()
        .createQuery("select r from Reservation r where "
        + " (r.end > :now) and "
        + " (r.reserver.authorizor = :user1 or r.resource.authorizor = :user2) and "
        + " (r.authorization = :pending) "
        + " order by r." + sortProperty
        + " " + (isAscending ? " ASC" : " DESC")
        );
    query.setFirstResult(first);
    query.setMaxResults(count);
    query.setParameter("now", new Date());
    query.setParameter("user1", user);
    query.setParameter("user2", user);
    query.setParameter("pending", Status.PENDING);
    
    return query.getResultList();
  }

  public List<Reservation> getPendingReservations(User user, int first, int count, String sortProperty, boolean isAscending) {
    Query query = getEntityManager()
        .createQuery("select r from Reservation r where "
        + " (r.end > :now) and "
        + " (r.reserver = :user) and "
        + " (r.authorization = :pending) "
        + " order by r." + sortProperty
        + " " + (isAscending ? " ASC" : " DESC")
        );
    query.setFirstResult(first);
    query.setMaxResults(count);
    query.setParameter("now", new Date());
    query.setParameter("user", user);
    query.setParameter("pending", Status.PENDING);
    
    return query.getResultList();
  }

  public Reservation getReservation(int id) {
    return getEntityManager().find(Reservation.class, id);
  }

  public void deleteReservation(Reservation reservation) {
    getEntityManager().remove(reservation);
  }
  
  protected EntityManager getEntityManager() {
    return manager;
  }

  @PersistenceContext
  public void setEntityManager(EntityManager em) {
    manager = em;
  }
}
